// Copyright 2018 The Feed Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.android.libraries.feed.piet.ui;

import static com.google.android.libraries.feed.common.Validators.checkState;

import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.ShapeDrawable;
import android.support.annotation.VisibleForTesting;

/** Generates a linear gradient according to CSS behavior */
class GradientShader extends ShapeDrawable.ShaderFactory {
  private final int[] colors;
  private final float[] stops;
  @VisibleForTesting final double angleRadians;
  @VisibleForTesting final double angleRadiansSmall;

  GradientShader(int[] colors, float[] stops, int angle) {
    checkState(
        colors.length == stops.length,
        "Mismatch: got %s colors and %s stops",
        colors.length,
        stops.length);
    this.colors = colors;
    this.stops = stops;
    this.angleRadians = Math.toRadians(angle % 360);
    this.angleRadiansSmall = Math.toRadians(angle % 90);
  }

  @Override
  public Shader resize(int width, int height) {
    RectF line = calculateGradientLine(width, height, angleRadians, angleRadiansSmall);

    return new android.graphics.LinearGradient(
        line.left, line.top, line.right, line.bottom, colors, stops, TileMode.REPEAT);
  }

  /** Returns a RectF as a shorthand for two points - start coords and end coords */
  @VisibleForTesting
  static RectF calculateGradientLine(
      int width, int height, double angleRadians, double angleRadiansSmall) {
    // See documentation on how CSS gradients work at:
    // https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient

    // This is Math. It's kind of like Magic.
    // You put numbers in, and the right gradients come out.
    // Note that on Android, the Y axis starts at the top and increases to the bottom, so some of
    // the math is backward.
    double length =
        Math.hypot(width / 2.0d, height / 2.0d)
            * Math.cos((Math.PI / 2) - Math.atan2(height, width) - angleRadiansSmall);
    float startX = (float) ((width / 2.0f) - length * Math.sin(angleRadians));
    float endX = (float) ((width / 2.0f) + length * Math.sin(angleRadians));
    float startY = (float) ((height / 2.0f) + length * Math.cos(angleRadians));
    float endY = (float) ((height / 2.0f) - length * Math.cos(angleRadians));

    return new RectF(startX, startY, endX, endY);
  }
}
